Introduction
Welcome to the third tutorial! In this lesson we are going to look at actual bird songs and learn how identify and seperate syllables. We will also look at some spike data at the end. By this time, you should be familiar with the basics of R, so there will be less syntax explanations. However, if you ever get confused or want to know more about a function, you can search for it using the Help tab ion the bottom right pane.
Note: We will be using the default graphics for some of our plots for conveniance, as spectro() uses default graphics and will not allow mulit-plots with ggplot2 generated graphs. There is a ggspectro() function, but it is complicated and we will not be using it.
Exercise 9: Sperate the syllables in a song
Lets load our data. Remember to load any necessary libraries before you begin.
library(seewave)
library(tuneR)
library(ggplot2)
library(gridExtra)
zf_data = readWave('zfinch.wav')
zfFs = zf_data@samp.rate
tim = seq(1/zfFs, length(zf_data@left)/zfFs, 1/zfFs)
We are going to separate the syllables by amplitude. The problem with the original signal is that it goes both up and down, so it is not possible to directly measure the amplitude. Below I describe two methods,“Strategy 1” and “Strategy 2”, that can be used to make a useful measure of the amplitude.
These two methods produce nearly the same result. The first method is to rectify the signal and low-pass filter, and the other is to use a function known as “Hilbert” and low-pass filter.
Both require that the signal be centered at zero. It is easy to do this by subtracting the mean from the original signal.
Lets use the mean() function…
zf_data_center = zf_data@left - mean(zf_data@left)
Strategy 1: Rectify and smooth
First lets take the absolute value using abs()…
rz = abs(zf_data_center)
Now we are going to smooth this signal using a median filter. R does this with the function runmed().
Before we do that lets set our variables. Here sms is the duration, in seconds, of the filter - I have set the default to 20 milliseconds. If sms is too long, then it will smooth out the signal too much. If sms is too short, then you will get spurious syllables. We will multiply sms with the sample rate, Fs. This will give us the number of samples for the duration that sms specifies.
sms = 0.020
zfFs = zf_data@samp.rate
The we apply our filter…
mrz = runmed(rz, Fs*sms)
We don’t need to do the next step, but it makes life a little easier later. Here we multiple by 1000 so that the values are ~ 1 rather than 0.001.
mrz = mrz*1000
Strategy 2: Hilbert and Smooth
This is almost identical to Strategy 1, but uses a nice function called hilbert(). This function is from the package seewave, which you should already have installed!
hz = abs(hilbert(rz, f = Fs))
Lets apply the same filter as above.
mhz = runmed(hz, Fs*sms)
mhz = mhz*1000
Time to plot. First the original signal…
zf_data_plot = data.frame(tim, zf_data@left)
ggplot(zf_data_plot, aes(x = tim, y = zf_data.left)) +
geom_line(color = "Blue") +
scale_y_continuous(name = "Frequency", breaks = c(27,127,227), limits = c(0,250)) +
scale_x_continuous(name = "Time")

…and the same signal after filtering.
ggplot() +
geom_line(aes(x = tim, y = mhz), color = "Green") +
geom_line(aes(x = tim, y = mrz), color = "Red") +
ylab("Time") +
xlab("Frequency")

Sepperate the Song into Syllables
Animals often produce signals that are composed of multiple parts. One strategy to understand the signal is to divide it into syllables. That is what we will do here - use an amplitude threshold to separate each part into distinct syllables.
We start by replotting the same data (mrz) from the last system, but the y axis will be on a log scale. This is acheived using the parameter scale_y_log10() which plots with the X axis normal and Y axis on a log scale.
ggplot() +
geom_line(aes(x = tim, y = mrz), color = "Red") +
scale_y_log10() +
theme(legend.position = "none")

Now that we’ve plotted our data, need to choose a threshold. We can either look at the plot and guess, or we can click on the plot and the computer will give us the coordinates where we clicked:
First strategy - I guess “900”!
thresh = 900
For clicking, we use the command locator() and specify how many clicks we are going to make, which is 1 in this case:
thresh = locator(1)
If we used locator(), then thresh will have two values, the x-value and the y-value of the click. We only want the y-value, which is in the second position, and hence we only want thresh[2]. We can do the following:
thresh = thresh[2]
Let’s replot with a line at the guessed threshold just to check. You might have to redo this section over and over again with different values for thresh until we get a value that we think will suffice.
And now we plot the line of our threshold :
ggplot() +
geom_line(aes(x = tim, y = mrz), color = "Red") +
geom_line(aes(x = tim, y = thresh), color = "Blue") +
scale_y_log10() +
theme(legend.position = "none")

NA
Now we use the which() command to get the part of the signal above the threshold. The variable syls will get the index numbers for each value of mrz that is larger than thresh:
syls = which(mrz > thresh)
Now we’re going to do something that will extract the starts and ends of each syllable. This seems complicated, but it is simple.
The first step is to make a list of zeros that is the length of the signal. We can use the rep() command:
zz = rep(0, length((zf_data@left)))
Let’s look at zz:
Replot the original signal and then plot zz.
grid.arrange(ggplot(zf_data_plot, aes(x = tim, y = zf_data.left)) +
geom_line(color = "Blue") +
scale_y_continuous(name = "Frequency", breaks = c(27,127,227), limits = c(0,250)) +
scale_x_continuous(name = "Time"),
ggplot() +
geom_line(aes(x = tim, y = zz, color = "Red")) +
theme(legend.position = "none"),
nrow = 2)

From above, syls is the list of values that are above the threshold - these are where the syllables are located. We will set zz to 1 for each of these values…
zz[syls] = 1
Now lets replot zz:
grid.arrange(ggplot(zf_data_plot, aes(x = tim, y = zf_data.left)) +
geom_line(color = "Blue") +
scale_y_continuous(name = "Frequency", breaks = c(27,127,227), limits = c(0,250)) +
scale_x_continuous(name = "Time"),
ggplot() +
geom_line(aes(x = tim, y = zz, color = "Red")) +
theme(legend.position = "none"),
nrow = 2)

Cool - these blocks roughly align with the syllables… but what we need are the start times and end times of the syllables. We will use a trick to get them…
diff takes the difference between adjacent values…
For example sample(2) - sample(1)…
yy = diff(zz)
yy_tim = tim[1:length(tim)-1]
Now let’s plot this. Starts are marked by 1 and ends are marked by -1.
ggplot() +
geom_line(aes(x = yy_tim, y = yy), color = "Blue")

Now we use the which() command to get the starts and the ends of each syllable. Notice that we use == to indicate when we are ASKING if values are equal, but use = to SET a variable to a value.
starts = which(yy == 1)
ends = which(yy == -1)
Now this is simple! We use a loop - the for command to get each syllable. Before we write our loop we will create some empty lists to hold our outputs.
Note: Loops are generally not recommended in R, but here it is fairly simple and makes ourlives easier.
syllable = c()
timmy = c()
timm = c()
for (i in seq(1,length(starts))) {
syllable[[i]] = zf_data@left[starts[i]:ends[i]]
timmy[[i]] = tim[starts[i]:ends[i]]
timm[[i]] = seq(1/Fs, (1 + ends[i] - starts[i])/Fs, 1/Fs)
}
syllable[1] will have the entire 1st syllable, syllable[2] will have the entire 2nd syllable, etc.
Note: timmy and timm are not necessary, but might be useful. timmy is the real times from the song. timm is the time of the syllable from 0
Just to show what has happened, here is a plot:
par(mfrow = c(2,4))
specplot(syllable[[4]], Fs = zfFs)
specplot(syllable[[5]], Fs = zfFs)
specplot(syllable[[6]], Fs = zfFs)
specplot(syllable[[7]], Fs = zfFs)
plot(timmy[[4]], syllable[[4]], type = "l", col = "Red")
plot(timmy[[5]], syllable[[5]], type = "l", col = "Red")
plot(timmy[[6]], syllable[[6]], type = "l", col = "Red")
plot(timmy[[7]], syllable[[7]], type = "l", col = "Red")

I picked syllables 4,5,6, and 7. You can pick others if you wish.
Now we will get the silent parts between syllables. This can be important because sometimes the time between signals is an independent signal. For example, in frogs the duration between calls determines the “pulse repetition rate”, which can indicate whether the call is a mate attraction signal or an aggressive signal.
The procedure is almost the same as for the syllables as above. What is different is that instead of copying the signal from zfData, we instead make pure silence by putting in a flat signal with an amplitude of 0.
We use a loop for each of the ends (which is the start of each silence).
To do this, make a list of zeros with the length of the interval and get the length of the interval by getting the difference between the end of the silence, which is the ‘start’ of the next syllable, and the start of the silence, which is the ‘end’ of the previous syllable. Complicated!?! Not really!
nop = c()
noptimmy = c()
noptimm = c()
for (j in seq(2,length(ends))) {
nop[[j]] = list(rep(127, times = starts[j] - ends[j-1]))
noptimmy[[j]] = tim[ends[j-1]:starts[j]]
noptimm[[j]] = seq(1/Fs, (1 + ends[j] - starts[j])/Fs, 1/Fs)
}
For review: You can easily get the data for each syllable.
syllable[[1]] is the data for the first syllable, and syllable[[2]] the second.
Let’s plot a syllable.
par(mfrow = c(1,2))
specplot(syllable[[7]], Fs = zfFs, ovlp = 90)
plot(timmy[[7]], syllable[[7]], type = "l", col = "Red")

How many syllables did we find??
length(syllable)
How long (duration) is syllable{7}?
length(syllable[[7]])/Fs
Exercise 10: Make a NEW song with the syllable order reversed
Here we are going to take each syllable, and instead of the normal order of ABCDEFG we will make the order GFEDCBA. Easy!
Here we are making a new variable, revordersong and we are putting the last syllable in there.
revordersong = syllable[length(syllable)]
revordersong = unlist(revordersong)
Now we will cycle though each syllable from the second to last to the first (we already have the last syllable from the above line.
The variables in the brackets will be concatenated. So [ revordersong nop syllable ] will make the previously defined revordersong followed by the silent period, which is then followed by the next syllable.
for (k in seq(length(syllable)-1, 1, -1)) {
revordersong = append(revordersong, unlist(nop[[k + 1]]))
revordersong = append(revordersong, unlist(syllable[[k]]))
}
This is nice to add - a time base for this new signal. We’ve seen this many times by now.
revtim = seq(1/Fs, length(revordersong)/Fs, 1/Fs)
p1 = ggplot() +
geom_line(aes(x = tim, y = zf_data@left), color = "Blue") +
scale_y_continuous(name = "Frequency", breaks = c(27,127,227), limits = c(0,250)) +
scale_x_continuous(name = "Time")
p2 = ggplot() +
geom_line(aes(x = revtim, y = revordersong), color = "Red") +
scale_y_continuous(name = "Frequency", breaks = c(27,127,227), limits = c(0,250)) +
scale_x_continuous(name = "Time")
grid.arrange(p1, p2, nrow = 2)

or we can show the spectra…
the orininal signal:
specplot(zf_data, Fs = zfFs)

reverse signal:
specplot(revordersong, Fs = zfFs)

Save the data to a wave file. You can then use Audacity to listen to it.
savewav(revordersong, Fs, "revordersong.wav")
Exercise 11: Make a NEW song with each syllable reversed but the syllable order normal
This is nearly identical to the previous excerise, but now the order is correct (ABCDEF) but each syllable is reversed.
Take the first syllable and put it in our new variable revsylsong…
revsylsong = unlist(syllable[length(syllable)])
Now reverse it – we saw how to do this in a previous excerise…
revsylsong = revsylsong[seq(to = 1, by = -1, length.out = length(revsylsong))]
Now do the same for the rest of the syllables, adding them to the new variable…
We use the temporary variable ra to do the reversing
for (l in seq(2, length(syllable), 1)) {
ra = unlist(syllable[[l]])
revsylsong = append(revsylsong, unlist(nop[[l]]))
revsylsong = append(revsylsong, unlist(ra[seq(to = 1, by = -1, length.out = length(syllable[[l]]))]))
}
and don’t forget about the time data…
revsyltim = seq(1/Fs, length(revsylsong)/Fs, 1/Fs)
Now we can make the same plots as last time, including spectrograms:
p3 = ggplot() +
geom_line(aes(x = tim, y = zf_data@left), color = "Blue") +
scale_y_continuous(name = "Frequency", breaks = c(27,127,227), limits = c(0,250)) +
scale_x_continuous(name = "Time")
p4 = ggplot() +
geom_line(aes(x = revsyltim, y = revsylsong), color = "Red") +
scale_y_continuous(name = "Frequency", breaks = c(27,127,227), limits = c(0,250)) +
scale_x_continuous(name = "Time")
grid.arrange(p3, p4, nrow = 2)

We can save the file as a .wav using the same process as in the last excercise.
savewav(revsylsong, FS, "revsylsong.wav")
With these tools you can make a wide range of arbitrary stimuli from your recordings of animal signals. These are powerful tolls for sensory electrophysiology.
Practice:Try to redo this exercise with the data in wcs.wav.
Exercise 12: Spikes
In this exercise we will take a recording of a neural activity (spikes!) and turn it into data that we can start to analyze in useful ways.
I have provided two sample files that contain some nice neural activity. They are very different from each other.
Read the first file, make the time data, and plot (easy by now).
a = readWave("spikes1.wav")
aFs = a@samp.rate
atim = seq(1/aFs, length(a)/aFs, 1/aFs)
ggplot() +
geom_line(aes(x = atim, y = a@left), colour = "Blue")

Lets zoom in on a second of the data…
Note: This can be done several ways. The first way is to us p + scale_x_continuous(limits = c(3,4)). This way will remove all points not plotted. You can use xlim(3,4) as a shorthand version of this same function. The method we use is p + coord_cartesian(xlim = c(3,4)), which PRESERVES data points not plotted. This may be relevant if you are fitting anything to the data.
ggplot() +
geom_line(aes(x = atim, y = a@left), colour = "Blue") +
coord_cartesian(xlim = c(3,4))

Now lets set and amplitude threshold for the spikes. Different recordings have different amplitudesfor spikes. Here I guessed 15000. Lets look at the plot to confirm.
athresh = 15000
We will use the which() function to get all of the data above the threshold.
spikes = which(a@left > athresh)
This is a complicated, but necessary step. Spikes are more than 1 or two milliseconds in duration. Thus, we want a threshold for the interval between spikes - anything less than that threshold is a mistake from the **which()* command.
Our threshold will be 1 millisecond: 0.001 seconds. Since are data is in samples and not in milliseconds, we’ll set our threshold to the sample rate * milliseconds.
diff is the difference between adjacent data points.
So, here we find all of the instances where the number of samples between spikes is greater than our threshold of 1 millisecond samples (which for Fs = 10000 is 10 samples).
spikes1 = spikes[which(diff(spikes) > 0.001 * aFs)]
How many spikes did we find?
length(spikes1)
[1] 223
What is the spike rate?
length(spikes1)/atim[length(atim)]
[1] 44.59911
Now let’s plot. We’ll plot a STAR at each time that a spike occurred. We’ll use the rep() function, except this time we will create a list of ones. This would be OK, but I decided to multiply it by the threshold so that the dots will be at the level of the threshold when we plot it. you will see…
ys = rep(1, length(spikes1)) * athresh
Now plot…
ggplot() +
geom_line(aes(x = atim, y = a@left), colour = "Blue")

…and add the stars.
ggplot() +
geom_line(aes(x = atim, y = a@left), colour = "Blue") +
geom_point(aes(x = atim[spikes1], y = ys), colour = "Red", size = 2, shape = 1)

Zoom in to get a better look!
ggplot() +
geom_line(aes(x = atim, y = a@left), colour = "Blue") +
geom_point(aes(x = atim[spikes1], y = ys), colour = "Red", size = 2, shape = 1) +
coord_cartesian(xlim = c(1,2))

We can go even further…
ggplot() +
geom_line(aes(x = atim, y = a@left), colour = "Blue") +
geom_point(aes(x = atim[spikes1], y = ys), colour = "Red", size = 2, shape = 1) +
coord_cartesian(xlim = c(1,1.25))

Another important measure is the time between spikes. This is very easy to get using the diff() command.
diff() will give use the number of samples between spikes, and then we divide by the sample rate to get the time, in seconds, between spikes.
intervals1 = diff(spikes1)/aFs
Here are some easy measures.
mean(intervals1)
[1] 0.02217613
pmax(intervals1)
[1] 0.0117 0.0374 0.0065 0.0029 0.0765 0.0307 0.0092 0.0516 0.0251 0.0046 0.0056 0.0524 0.0062
[14] 0.0702 0.0044 0.0773 0.0031 0.0047 0.0025 0.0197 0.0182 0.0035 0.0033 0.0040 0.0411 0.0046
[27] 0.0030 0.0287 0.0036 0.0045 0.0579 0.0047 0.0034 0.1463 0.0027 0.0055 0.0037 0.0036 0.0312
[40] 0.0186 0.0046 0.0046 0.0031 0.0232 0.0035 0.0526 0.0056 0.0028 0.0078 0.0344 0.0058 0.0717
[53] 0.0028 0.0033 0.0059 0.0543 0.0032 0.0028 0.0612 0.0042 0.0043 0.0025 0.0551 0.0030 0.0031
[66] 0.0058 0.0241 0.0028 0.0252 0.0024 0.0050 0.0884 0.0061 0.0112 0.0543 0.0089 0.0080 0.0048
[79] 0.0539 0.0315 0.0044 0.0038 0.0946 0.0349 0.0023 0.0063 0.0039 0.0358 0.0043 0.0273 0.0022
[92] 0.0070 0.1410 0.0037 0.0351 0.0046 0.0130 0.0044 0.0061 0.0210 0.0026 0.0055 0.0025 0.0369
[105] 0.0300 0.0023 0.0058 0.0073 0.1415 0.0320 0.0695 0.0026 0.0221 0.0045 0.0105 0.0067 0.0045
[118] 0.0682 0.0027 0.0051 0.0027 0.0043 0.0759 0.0051 0.0964 0.0385 0.0035 0.0048 0.0037 0.0582
[131] 0.0025 0.0055 0.0048 0.0815 0.0219 0.0028 0.0050 0.0306 0.0036 0.0027 0.0053 0.0323 0.0074
[144] 0.0042 0.0582 0.0043 0.0550 0.0068 0.0375 0.0027 0.0071 0.0306 0.0277 0.0282 0.0027 0.0031
[157] 0.0684 0.0034 0.0060 0.1088 0.0046 0.0030 0.0038 0.0816 0.0194 0.0022 0.0029 0.0042 0.0920
[170] 0.0031 0.0054 0.0042 0.0029 0.0328 0.0600 0.0656 0.0345 0.0034 0.0050 0.0055 0.0158 0.0041
[183] 0.0719 0.0043 0.0397 0.0036 0.0035 0.0246 0.0076 0.0022 0.0046 0.0032 0.1378 0.0025 0.0462
[196] 0.0043 0.0067 0.0026 0.0055 0.0319 0.0057 0.0942 0.0514 0.0047 0.0057 0.0369 0.0054 0.0412
[209] 0.0159 0.0026 0.0058 0.0031 0.0023 0.0043 0.0567 0.0255 0.0039 0.1530 0.0034 0.0068 0.0033
[222] 0.0082
pmin(intervals1)
[1] 0.0117 0.0374 0.0065 0.0029 0.0765 0.0307 0.0092 0.0516 0.0251 0.0046 0.0056 0.0524 0.0062
[14] 0.0702 0.0044 0.0773 0.0031 0.0047 0.0025 0.0197 0.0182 0.0035 0.0033 0.0040 0.0411 0.0046
[27] 0.0030 0.0287 0.0036 0.0045 0.0579 0.0047 0.0034 0.1463 0.0027 0.0055 0.0037 0.0036 0.0312
[40] 0.0186 0.0046 0.0046 0.0031 0.0232 0.0035 0.0526 0.0056 0.0028 0.0078 0.0344 0.0058 0.0717
[53] 0.0028 0.0033 0.0059 0.0543 0.0032 0.0028 0.0612 0.0042 0.0043 0.0025 0.0551 0.0030 0.0031
[66] 0.0058 0.0241 0.0028 0.0252 0.0024 0.0050 0.0884 0.0061 0.0112 0.0543 0.0089 0.0080 0.0048
[79] 0.0539 0.0315 0.0044 0.0038 0.0946 0.0349 0.0023 0.0063 0.0039 0.0358 0.0043 0.0273 0.0022
[92] 0.0070 0.1410 0.0037 0.0351 0.0046 0.0130 0.0044 0.0061 0.0210 0.0026 0.0055 0.0025 0.0369
[105] 0.0300 0.0023 0.0058 0.0073 0.1415 0.0320 0.0695 0.0026 0.0221 0.0045 0.0105 0.0067 0.0045
[118] 0.0682 0.0027 0.0051 0.0027 0.0043 0.0759 0.0051 0.0964 0.0385 0.0035 0.0048 0.0037 0.0582
[131] 0.0025 0.0055 0.0048 0.0815 0.0219 0.0028 0.0050 0.0306 0.0036 0.0027 0.0053 0.0323 0.0074
[144] 0.0042 0.0582 0.0043 0.0550 0.0068 0.0375 0.0027 0.0071 0.0306 0.0277 0.0282 0.0027 0.0031
[157] 0.0684 0.0034 0.0060 0.1088 0.0046 0.0030 0.0038 0.0816 0.0194 0.0022 0.0029 0.0042 0.0920
[170] 0.0031 0.0054 0.0042 0.0029 0.0328 0.0600 0.0656 0.0345 0.0034 0.0050 0.0055 0.0158 0.0041
[183] 0.0719 0.0043 0.0397 0.0036 0.0035 0.0246 0.0076 0.0022 0.0046 0.0032 0.1378 0.0025 0.0462
[196] 0.0043 0.0067 0.0026 0.0055 0.0319 0.0057 0.0942 0.0514 0.0047 0.0057 0.0369 0.0054 0.0412
[209] 0.0159 0.0026 0.0058 0.0031 0.0023 0.0043 0.0567 0.0255 0.0039 0.1530 0.0034 0.0068 0.0033
[222] 0.0082
But looking at the plot we see that the spikes are in “bursts” rather than randomly spread. If they were randomly timed, we would find a random distribution of the intervals between spikes. Lets take a look using a histogram…
ggplot() +
geom_histogram(aes(x = intervals1), bins = 100)

For these data we see that there are many many intervals below 0.015 (15 milliseconds), and a spread of longer intervals. This plot is a very good example of a “bursty” neuron.
The other data, spikes2.wav, is very very different.
Please do the same analysis for those data as you did for spikes1.wav. Use different variable names like changing spikes1 to spikes2 and interval1 to interval2 so that you can plot a histogram just like we did above. Try plotting both histograms in the same frame.
LS0tCnRpdGxlOiAiUiBUcmFpbmluZyAzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCiMgSW50cm9kdWN0aW9uIAoKV2VsY29tZSB0byB0aGUgdGhpcmQgdHV0b3JpYWwhIEluIHRoaXMgbGVzc29uIHdlIGFyZSBnb2luZyB0byBsb29rIGF0IGFjdHVhbCBiaXJkIHNvbmdzIGFuZCBsZWFybiBob3cgaWRlbnRpZnkgYW5kIHNlcGVyYXRlIHN5bGxhYmxlcy4gV2Ugd2lsbCBhbHNvIGxvb2sgYXQgc29tZSBzcGlrZSBkYXRhIGF0IHRoZSBlbmQuIEJ5IHRoaXMgdGltZSwgeW91IHNob3VsZCBiZSBmYW1pbGlhciB3aXRoIHRoZSBiYXNpY3Mgb2YgUiwgc28gdGhlcmUgd2lsbCBiZSBsZXNzIHN5bnRheCBleHBsYW5hdGlvbnMuIEhvd2V2ZXIsIGlmIHlvdSBldmVyIGdldCBjb25mdXNlZCBvciB3YW50IHRvIGtub3cgbW9yZSBhYm91dCBhIGZ1bmN0aW9uLCB5b3UgY2FuIHNlYXJjaCBmb3IgaXQgdXNpbmcgdGhlIEhlbHAgdGFiIGlvbiB0aGUgYm90dG9tIHJpZ2h0IHBhbmUuIAoKKk5vdGU6IFdlIHdpbGwgYmUgdXNpbmcgdGhlIGRlZmF1bHQgZ3JhcGhpY3MgZm9yIHNvbWUgb2Ygb3VyIHBsb3RzIGZvciBjb252ZW5pYW5jZSwgYXMgKipzcGVjdHJvKCkqKiB1c2VzIGRlZmF1bHQgZ3JhcGhpY3MgYW5kIHdpbGwgbm90IGFsbG93IG11bGl0LXBsb3RzIHdpdGggZ2dwbG90MiBnZW5lcmF0ZWQgZ3JhcGhzLiBUaGVyZSBpcyBhICoqZ2dzcGVjdHJvKCkqKiBmdW5jdGlvbiwgYnV0IGl0IGlzIGNvbXBsaWNhdGVkIGFuZCB3ZSB3aWxsIG5vdCBiZSB1c2luZyBpdC4qICAKCiMgRXhlcmNpc2UgOTogU3BlcmF0ZSB0aGUgc3lsbGFibGVzIGluIGEgc29uZwoKTGV0cyBsb2FkIG91ciBkYXRhLiBSZW1lbWJlciB0byBsb2FkIGFueSBuZWNlc3NhcnkgbGlicmFyaWVzIGJlZm9yZSB5b3UgYmVnaW4uIAoKYGBge3J9CmxpYnJhcnkoc2Vld2F2ZSkKbGlicmFyeSh0dW5lUikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGdyaWRFeHRyYSkKbGlicmFyeShvY2UpCmxpYnJhcnkoc2lnbmFsKQp6Zl9kYXRhID0gcmVhZFdhdmUoJ3pmaW5jaC53YXYnKQp6ZkZzID0gemZfZGF0YUBzYW1wLnJhdGUKdGltID0gc2VxKDEvemZGcywgbGVuZ3RoKHpmX2RhdGFAbGVmdCkvemZGcywgMS96ZkZzKQpgYGAKCldlIGFyZSBnb2luZyB0byBzZXBhcmF0ZSB0aGUgc3lsbGFibGVzIGJ5IGFtcGxpdHVkZS4gVGhlIHByb2JsZW0gd2l0aCB0aGUgb3JpZ2luYWwgc2lnbmFsIGlzIHRoYXQgaXQgZ29lcyBib3RoIHVwIGFuZCBkb3duLCBzbyBpdCBpcyBub3QgcG9zc2libGUgdG8gZGlyZWN0bHkgbWVhc3VyZSB0aGUgYW1wbGl0dWRlLiBCZWxvdyBJIGRlc2NyaWJlIHR3byBtZXRob2RzLCJTdHJhdGVneSAxIiBhbmQgIlN0cmF0ZWd5IDIiLCB0aGF0IGNhbiBiZSB1c2VkIHRvIG1ha2UgYSB1c2VmdWwgbWVhc3VyZSBvZiB0aGUgYW1wbGl0dWRlLgoKVGhlc2UgdHdvIG1ldGhvZHMgcHJvZHVjZSBuZWFybHkgdGhlIHNhbWUgcmVzdWx0LiBUaGUgZmlyc3QgbWV0aG9kIGlzIHRvIHJlY3RpZnkgdGhlIHNpZ25hbCBhbmQgbG93LXBhc3MgZmlsdGVyLCBhbmQgdGhlIG90aGVyIGlzIHRvIHVzZSBhIGZ1bmN0aW9uIGtub3duIGFzICJIaWxiZXJ0IiBhbmQgbG93LXBhc3MgZmlsdGVyLgoKQm90aCByZXF1aXJlIHRoYXQgdGhlIHNpZ25hbCBiZSBjZW50ZXJlZCBhdCB6ZXJvLiBJdCBpcyBlYXN5IHRvIGRvIHRoaXMgYnkgc3VidHJhY3RpbmcgdGhlIG1lYW4gZnJvbSB0aGUgb3JpZ2luYWwgc2lnbmFsLgoKTGV0cyB1c2UgdGhlICoqbWVhbigpKiogZnVuY3Rpb24uLi4KCmBgYHtyfQp6Zl9kYXRhX2NlbnRlciA9IHpmX2RhdGFAbGVmdCAtIG1lYW4oemZfZGF0YUBsZWZ0KQpgYGAKCiMjIFN0cmF0ZWd5IDE6IFJlY3RpZnkgYW5kIHNtb290aAoKRmlyc3QgbGV0cyB0YWtlIHRoZSBhYnNvbHV0ZSB2YWx1ZSB1c2luZyAqKmFicygpKiouLi4KCmBgYHtyfQpyeiA9IGFicyh6Zl9kYXRhX2NlbnRlcikKYGBgCgpOb3cgd2UgYXJlIGdvaW5nIHRvIHNtb290aCB0aGlzIHNpZ25hbCB1c2luZyBhIG1lZGlhbiBmaWx0ZXIuIFIgZG9lcyB0aGlzIHdpdGggdGhlIGZ1bmN0aW9uICoqcnVubWVkKCkqKi4gCgpCZWZvcmUgd2UgZG8gdGhhdCBsZXRzIHNldCBvdXIgdmFyaWFibGVzLiBIZXJlICoqc21zKiogaXMgdGhlIGR1cmF0aW9uLCBpbiBzZWNvbmRzLCBvZiB0aGUgZmlsdGVyIC0gSSBoYXZlIHNldCB0aGUgZGVmYXVsdCB0byAyMCBtaWxsaXNlY29uZHMuIElmICoqc21zKiogaXMgdG9vIGxvbmcsIHRoZW4gaXQgd2lsbCBzbW9vdGggb3V0IHRoZSBzaWduYWwgdG9vIG11Y2guIElmICoqc21zKiogaXMgdG9vIHNob3J0LCB0aGVuIHlvdSB3aWxsIGdldCBzcHVyaW91cyBzeWxsYWJsZXMuIFdlIHdpbGwgbXVsdGlwbHkgKipzbXMqKiB3aXRoIHRoZSBzYW1wbGUgcmF0ZSwgKipGcyoqLiBUaGlzIHdpbGwgZ2l2ZSB1cyB0aGUgbnVtYmVyIG9mIHNhbXBsZXMgZm9yIHRoZSBkdXJhdGlvbiB0aGF0ICoqc21zKiogc3BlY2lmaWVzLgoKYGBge3J9CnNtcyA9IDAuMDIwCnpmRnMgPSB6Zl9kYXRhQHNhbXAucmF0ZQpgYGAKClRoZSB3ZSBhcHBseSBvdXIgZmlsdGVyLi4uCgpgYGB7cn0KbXJ6ID0gcnVubWVkKHJ6LCBGcypzbXMpCmBgYAoKV2UgZG9uJ3QgbmVlZCB0byBkbyB0aGUgbmV4dCBzdGVwLCBidXQgaXQgbWFrZXMgbGlmZSBhIGxpdHRsZSBlYXNpZXIgbGF0ZXIuIEhlcmUgd2UgbXVsdGlwbGUgYnkgMTAwMCBzbyB0aGF0IHRoZSB2YWx1ZXMgYXJlIH4gMSByYXRoZXIgdGhhbiAwLjAwMS4KCmBgYHtyfQptcnogPSBtcnoqMTAwMApgYGAKCiMjIFN0cmF0ZWd5IDI6IEhpbGJlcnQgYW5kIFNtb290aAoKVGhpcyBpcyBhbG1vc3QgaWRlbnRpY2FsIHRvIFN0cmF0ZWd5IDEsIGJ1dCB1c2VzIGEgbmljZSBmdW5jdGlvbiBjYWxsZWQgKipoaWxiZXJ0KCkqKi4gVGhpcyBmdW5jdGlvbiBpcyBmcm9tIHRoZSBwYWNrYWdlICoqc2Vld2F2ZSoqLCB3aGljaCB5b3Ugc2hvdWxkIGFscmVhZHkgaGF2ZSBpbnN0YWxsZWQhCgpgYGB7cn0KaHogPSBhYnMoaGlsYmVydChyeiwgZiA9IEZzKSkKYGBgCgpMZXRzIGFwcGx5IHRoZSBzYW1lIGZpbHRlciBhcyBhYm92ZS4KCmBgYHtyfQptaHogPSBydW5tZWQoaHosIEZzKnNtcykKbWh6ID0gbWh6KjEwMDAKYGBgCgpUaW1lIHRvIHBsb3QuIEZpcnN0IHRoZSBvcmlnaW5hbCBzaWduYWwuLi4KCmBgYHtyfQp6Zl9kYXRhX3Bsb3QgPSBkYXRhLmZyYW1lKHRpbSwgemZfZGF0YUBsZWZ0KQoKZ2dwbG90KHpmX2RhdGFfcGxvdCwgYWVzKHggPSB0aW0sIHkgPSB6Zl9kYXRhLmxlZnQpKSArIAogIGdlb21fbGluZShjb2xvciA9ICJCbHVlIikgKwogIHNjYWxlX3lfY29udGludW91cyhuYW1lID0gIkZyZXF1ZW5jeSIsIGJyZWFrcyA9IGMoMjcsMTI3LDIyNyksIGxpbWl0cyA9IGMoMCwyNTApKSArCiAgc2NhbGVfeF9jb250aW51b3VzKG5hbWUgPSAiVGltZSIpCgpgYGAKCi4uLmFuZCB0aGUgc2FtZSBzaWduYWwgYWZ0ZXIgZmlsdGVyaW5nLgoKYGBge3J9CmdncGxvdCgpICsKICBnZW9tX2xpbmUoYWVzKHggPSB0aW0sIHkgPSBtaHopLCBjb2xvciA9ICJHcmVlbiIpICsKICBnZW9tX2xpbmUoYWVzKHggPSB0aW0sIHkgPSBtcnopLCBjb2xvciA9ICJSZWQiKSArCiAgeWxhYigiVGltZSIpICsKICB4bGFiKCJGcmVxdWVuY3kiKQpgYGAKCgojIyBTZXBwZXJhdGUgdGhlIFNvbmcgaW50byBTeWxsYWJsZXMgCgoKQW5pbWFscyBvZnRlbiBwcm9kdWNlIHNpZ25hbHMgdGhhdCBhcmUgY29tcG9zZWQgb2YgbXVsdGlwbGUgcGFydHMuIE9uZSBzdHJhdGVneSB0byB1bmRlcnN0YW5kIHRoZSBzaWduYWwgaXMgdG8gZGl2aWRlIGl0IGludG8gc3lsbGFibGVzLiBUaGF0IGlzIHdoYXQgd2Ugd2lsbCBkbyBoZXJlIC0gdXNlIGFuIGFtcGxpdHVkZSB0aHJlc2hvbGQgdG8gc2VwYXJhdGUgZWFjaCBwYXJ0IGludG8gZGlzdGluY3Qgc3lsbGFibGVzLiAKCldlIHN0YXJ0IGJ5IHJlcGxvdHRpbmcgdGhlIHNhbWUgZGF0YSAoKiptcnoqKikgZnJvbSB0aGUgbGFzdCBzeXN0ZW0sIGJ1dCB0aGUgKip5IGF4aXMqKiB3aWxsIGJlIG9uIGEgbG9nIHNjYWxlLiBUaGlzIGlzIGFjaGVpdmVkIHVzaW5nIHRoZSBwYXJhbWV0ZXIgKipzY2FsZV95X2xvZzEwKCkqKiB3aGljaCBwbG90cyB3aXRoIHRoZSBYIGF4aXMgbm9ybWFsIGFuZCBZIGF4aXMgb24gYSBsb2cgc2NhbGUuIAoKYGBge3J9CgpnZ3Bsb3QoKSArCiAgZ2VvbV9saW5lKGFlcyh4ID0gdGltLCB5ID0gbXJ6KSwgY29sb3IgPSAiUmVkIikgKwogIHNjYWxlX3lfbG9nMTAoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCk5vdyB0aGF0IHdlJ3ZlIHBsb3R0ZWQgb3VyIGRhdGEsIG5lZWQgdG8gY2hvb3NlIGEgdGhyZXNob2xkLiBXZSBjYW4gZWl0aGVyIGxvb2sgYXQgdGhlIHBsb3QgYW5kIGd1ZXNzLCBvciB3ZSBjYW4gY2xpY2sgb24gdGhlIHBsb3QgYW5kIHRoZSBjb21wdXRlciB3aWxsIGdpdmUgdXMgdGhlIGNvb3JkaW5hdGVzIHdoZXJlIHdlIGNsaWNrZWQ6CgojIyMgRmlyc3Qgc3RyYXRlZ3kgLSBJIGd1ZXNzICI5MDAiIQoKYGBge3J9CnRocmVzaCA9IDkwMApgYGAKCkZvciBjbGlja2luZywgd2UgdXNlIHRoZSBjb21tYW5kICoqbG9jYXRvcigpKiogYW5kIHNwZWNpZnkgaG93IG1hbnkgY2xpY2tzIHdlIGFyZSBnb2luZyB0byBtYWtlLCB3aGljaCBpcyAxIGluIHRoaXMgY2FzZToKCnRocmVzaCA9IGxvY2F0b3IoMSkKCgpJZiB3ZSB1c2VkICoqbG9jYXRvcigpKiosIHRoZW4gKip0aHJlc2gqKiB3aWxsIGhhdmUgdHdvIHZhbHVlcywgdGhlIHgtdmFsdWUgYW5kIHRoZSB5LXZhbHVlIG9mIHRoZSBjbGljay4gV2Ugb25seSB3YW50IHRoZSB5LXZhbHVlLCB3aGljaCBpcyBpbiB0aGUgc2Vjb25kIHBvc2l0aW9uLCBhbmQgaGVuY2Ugd2Ugb25seSB3YW50CioqdGhyZXNoWzJdKiouIFdlIGNhbiBkbyB0aGUgZm9sbG93aW5nOgoKdGhyZXNoID0gdGhyZXNoWzJdCgpMZXQncyByZXBsb3Qgd2l0aCBhIGxpbmUgYXQgdGhlIGd1ZXNzZWQgdGhyZXNob2xkIGp1c3QgdG8gY2hlY2suIFlvdSBtaWdodCBoYXZlIHRvIHJlZG8gdGhpcyBzZWN0aW9uIG92ZXIgYW5kIG92ZXIgYWdhaW4gd2l0aCBkaWZmZXJlbnQgdmFsdWVzIGZvciAqKnRocmVzaCoqIHVudGlsIHdlIGdldCBhIHZhbHVlIHRoYXQgd2UgdGhpbmsgd2lsbCBzdWZmaWNlLgoKQW5kIG5vdyB3ZSBwbG90IHRoZSBsaW5lIG9mIG91ciB0aHJlc2hvbGQgOgoKYGBge3J9CmdncGxvdCgpICsKICBnZW9tX2xpbmUoYWVzKHggPSB0aW0sIHkgPSBtcnopLCBjb2xvciA9ICJSZWQiKSArCiAgZ2VvbV9saW5lKGFlcyh4ID0gdGltLCB5ID0gdGhyZXNoKSwgY29sb3IgPSAiQmx1ZSIpICsKICBzY2FsZV95X2xvZzEwKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKICAKYGBgCgpOb3cgd2UgdXNlIHRoZSAqKndoaWNoKCkqKiBjb21tYW5kIHRvIGdldCB0aGUgcGFydCBvZiB0aGUgc2lnbmFsIGFib3ZlIHRoZSB0aHJlc2hvbGQuIFRoZSB2YXJpYWJsZSAqKnN5bHMqKiB3aWxsIGdldCB0aGUgaW5kZXggbnVtYmVycyBmb3IgZWFjaCB2YWx1ZSBvZiAqKm1yeioqIHRoYXQgaXMgbGFyZ2VyIHRoYW4gKip0aHJlc2gqKjoKCmBgYHtyfQpzeWxzID0gd2hpY2gobXJ6ID4gdGhyZXNoKQpgYGAKCk5vdyB3ZSdyZSBnb2luZyB0byBkbyBzb21ldGhpbmcgdGhhdCB3aWxsIGV4dHJhY3QgdGhlIHN0YXJ0cyBhbmQgZW5kcyBvZiBlYWNoIHN5bGxhYmxlLiBUaGlzIHNlZW1zIGNvbXBsaWNhdGVkLCBidXQgaXQgaXMgc2ltcGxlLgoKVGhlIGZpcnN0IHN0ZXAgaXMgdG8gbWFrZSBhIGxpc3Qgb2YgemVyb3MgdGhhdCBpcyB0aGUgbGVuZ3RoIG9mIHRoZSBzaWduYWwuIFdlIGNhbiB1c2UgdGhlICoqcmVwKCkqKiBjb21tYW5kOgoKYGBge3J9Cnp6ID0gcmVwKDAsIGxlbmd0aCgoemZfZGF0YUBsZWZ0KSkpCmBgYAoKTGV0J3MgbG9vayBhdCAqKnp6Kio6CgpSZXBsb3QgdGhlIG9yaWdpbmFsIHNpZ25hbCBhbmQgdGhlbiBwbG90ICoqenoqKi4KCmBgYHtyfQoKZ3JpZC5hcnJhbmdlKGdncGxvdCh6Zl9kYXRhX3Bsb3QsIGFlcyh4ID0gdGltLCB5ID0gemZfZGF0YS5sZWZ0KSkgKyAKICAgICAgICAgICAgICAgZ2VvbV9saW5lKGNvbG9yID0gIkJsdWUiKSArCiAgICAgICAgICAgICAgIHNjYWxlX3lfY29udGludW91cyhuYW1lID0gIkZyZXF1ZW5jeSIsIGJyZWFrcyA9IGMoMjcsMTI3LDIyNyksIGxpbWl0cyA9IGMoMCwyNTApKSArCiAgICAgICAgICAgICAgIHNjYWxlX3hfY29udGludW91cyhuYW1lID0gIlRpbWUiKSwKICAgICAgICAgICAgIGdncGxvdCgpICsKICAgICAgICAgICAgICAgZ2VvbV9saW5lKGFlcyh4ID0gdGltLCB5ID0genosIGNvbG9yID0gIlJlZCIpKSArCiAgICAgICAgICAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiksIAogICAgICAgICAgICAgbnJvdyA9IDIpCgpgYGAKCkZyb20gYWJvdmUsICoqc3lscyoqIGlzIHRoZSBsaXN0IG9mIHZhbHVlcyB0aGF0IGFyZSBhYm92ZSB0aGUgdGhyZXNob2xkIC0gdGhlc2UgYXJlIHdoZXJlIHRoZSBzeWxsYWJsZXMgYXJlIGxvY2F0ZWQuIFdlIHdpbGwgc2V0ICoqenoqKiB0byAxIGZvciBlYWNoIG9mIHRoZXNlIHZhbHVlcy4uLgoKYGBge3J9Cnp6W3N5bHNdID0gMQpgYGAKCk5vdyBsZXRzIHJlcGxvdCAqKnp6Kio6CgpgYGB7cn0KZ3JpZC5hcnJhbmdlKGdncGxvdCh6Zl9kYXRhX3Bsb3QsIGFlcyh4ID0gdGltLCB5ID0gemZfZGF0YS5sZWZ0KSkgKyAKICAgICAgICAgICAgICAgZ2VvbV9saW5lKGNvbG9yID0gIkJsdWUiKSArCiAgICAgICAgICAgICAgIHNjYWxlX3lfY29udGludW91cyhuYW1lID0gIkZyZXF1ZW5jeSIsIGJyZWFrcyA9IGMoMjcsMTI3LDIyNyksIGxpbWl0cyA9IGMoMCwyNTApKSArCiAgICAgICAgICAgICAgIHNjYWxlX3hfY29udGludW91cyhuYW1lID0gIlRpbWUiKSwKICAgICAgICAgICAgIGdncGxvdCgpICsKICAgICAgICAgICAgICAgZ2VvbV9saW5lKGFlcyh4ID0gdGltLCB5ID0genosIGNvbG9yID0gIlJlZCIpKSArCiAgICAgICAgICAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiksIAogICAgICAgICAgICAgbnJvdyA9IDIpCgpgYGAKCgpDb29sIC0gdGhlc2UgYmxvY2tzIHJvdWdobHkgYWxpZ24gd2l0aCB0aGUgc3lsbGFibGVzLi4uIGJ1dCB3aGF0IHdlIG5lZWQgYXJlIHRoZSBzdGFydCB0aW1lcyBhbmQgZW5kIHRpbWVzIG9mIHRoZSBzeWxsYWJsZXMuIFdlIHdpbGwgdXNlIGEgdHJpY2sgdG8gZ2V0IHRoZW0uLi4KCioqZGlmZioqIHRha2VzIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gYWRqYWNlbnQgdmFsdWVzLi4uCgoqRm9yIGV4YW1wbGUgc2FtcGxlKDIpIC0gc2FtcGxlKDEpLi4uKgoKYGBge3J9Cnl5ID0gZGlmZih6eikKeXlfdGltID0gdGltWzE6bGVuZ3RoKHRpbSktMV0KYGBgCgpOb3cgbGV0J3MgcGxvdCB0aGlzLiBTdGFydHMgYXJlIG1hcmtlZCBieSAqKjEqKiBhbmQgZW5kcyBhcmUgbWFya2VkIGJ5ICoqLTEqKi4KCmBgYHtyfQpnZ3Bsb3QoKSArCiAgZ2VvbV9saW5lKGFlcyh4ID0geXlfdGltLCB5ID0geXkpLCBjb2xvciA9ICJCbHVlIikKYGBgCiAKCk5vdyB3ZSB1c2UgdGhlICoqd2hpY2goKSoqIGNvbW1hbmQgdG8gZ2V0IHRoZSAqc3RhcnRzKiBhbmQgdGhlICplbmRzKiBvZiBlYWNoIHN5bGxhYmxlLiBOb3RpY2UgdGhhdCB3ZSB1c2UgKio9PSoqIHRvIGluZGljYXRlIHdoZW4gd2UgYXJlICoqQVNLSU5HKiogaWYgdmFsdWVzIGFyZSBlcXVhbCwgYnV0IHVzZSAqKj0qKiB0byAqKlNFVCoqIGEgdmFyaWFibGUgdG8gYSB2YWx1ZS4KCmBgYHtyfQpzdGFydHMgPSB3aGljaCh5eSA9PSAxKQplbmRzID0gd2hpY2goeXkgPT0gLTEpCmBgYAoKCk5vdyB0aGlzIGlzIHNpbXBsZSEgV2UgdXNlIGEgbG9vcCAtIHRoZSAqKmZvcioqIGNvbW1hbmQgdG8gZ2V0IGVhY2ggc3lsbGFibGUuIEJlZm9yZSB3ZSB3cml0ZSBvdXIgbG9vcCB3ZSB3aWxsIGNyZWF0ZSBzb21lIGVtcHR5IGxpc3RzIHRvIGhvbGQgb3VyIG91dHB1dHMuCgoqTm90ZTogTG9vcHMgYXJlIGdlbmVyYWxseSBub3QgcmVjb21tZW5kZWQgaW4gUiwgYnV0IGhlcmUgaXQgaXMgZmFpcmx5IHNpbXBsZSBhbmQgbWFrZXMgb3VybGl2ZXMgZWFzaWVyLioKCmBgYHtyfQpzeWxsYWJsZSA9IGMoKQp0aW1teSA9IGMoKQp0aW1tID0gYygpCgoKZm9yIChpIGluIHNlcSgxLGxlbmd0aChzdGFydHMpKSkgewoKICBzeWxsYWJsZVtbaV1dID0gemZfZGF0YUBsZWZ0W3N0YXJ0c1tpXTplbmRzW2ldXQogIHRpbW15W1tpXV0gPSB0aW1bc3RhcnRzW2ldOmVuZHNbaV1dCiAgdGltbVtbaV1dID0gc2VxKDEvRnMsICgxICsgZW5kc1tpXSAtIHN0YXJ0c1tpXSkvRnMsIDEvRnMpCn0KYGBgCgoqKnN5bGxhYmxlWzFdKiogd2lsbCBoYXZlIHRoZSBlbnRpcmUgMXN0IHN5bGxhYmxlLCAqKnN5bGxhYmxlWzJdKiogd2lsbCBoYXZlIHRoZSBlbnRpcmUgMm5kIHN5bGxhYmxlLCBldGMuCgoqTm90ZTogdGltbXkgYW5kIHRpbW0gYXJlIG5vdCBuZWNlc3NhcnksIGJ1dCBtaWdodCBiZSB1c2VmdWwuCioqdGltbXkqKiBpcyB0aGUgcmVhbCB0aW1lcyBmcm9tIHRoZSBzb25nLgoqKnRpbW0qKiBpcyB0aGUgdGltZSBvZiB0aGUgc3lsbGFibGUgZnJvbSAwKgoKSnVzdCB0byBzaG93IHdoYXQgaGFzIGhhcHBlbmVkLCBoZXJlIGlzIGEgcGxvdDoKCmBgYHtyfQoKcGFyKG1mcm93ID0gYygyLDQpKQpzcGVjcGxvdChzeWxsYWJsZVtbNF1dLCBGcyA9IHpmRnMpCnNwZWNwbG90KHN5bGxhYmxlW1s1XV0sIEZzID0gemZGcykKc3BlY3Bsb3Qoc3lsbGFibGVbWzZdXSwgRnMgPSB6ZkZzKQpzcGVjcGxvdChzeWxsYWJsZVtbN11dLCBGcyA9IHpmRnMpCgpwbG90KHRpbW15W1s0XV0sIHN5bGxhYmxlW1s0XV0sIHR5cGUgPSAibCIsIGNvbCA9ICJSZWQiKQpwbG90KHRpbW15W1s1XV0sIHN5bGxhYmxlW1s1XV0sIHR5cGUgPSAibCIsIGNvbCA9ICJSZWQiKQpwbG90KHRpbW15W1s2XV0sIHN5bGxhYmxlW1s2XV0sIHR5cGUgPSAibCIsIGNvbCA9ICJSZWQiKQpwbG90KHRpbW15W1s3XV0sIHN5bGxhYmxlW1s3XV0sIHR5cGUgPSAibCIsIGNvbCA9ICJSZWQiKQpgYGAKCkkgcGlja2VkIHN5bGxhYmxlcyA0LDUsNiwgYW5kIDcuIFlvdSBjYW4gcGljayBvdGhlcnMgaWYgeW91IHdpc2guCgoKTm93IHdlIHdpbGwgZ2V0IHRoZSBzaWxlbnQgcGFydHMgYmV0d2VlbiBzeWxsYWJsZXMuIFRoaXMgY2FuIGJlIGltcG9ydGFudCBiZWNhdXNlIHNvbWV0aW1lcyB0aGUgdGltZSBiZXR3ZWVuIHNpZ25hbHMgaXMgYW4gaW5kZXBlbmRlbnQgc2lnbmFsLiBGb3IgZXhhbXBsZSwgaW4gZnJvZ3MgdGhlIGR1cmF0aW9uIGJldHdlZW4gY2FsbHMgZGV0ZXJtaW5lcyB0aGUgInB1bHNlIHJlcGV0aXRpb24gcmF0ZSIsIHdoaWNoIGNhbiBpbmRpY2F0ZSB3aGV0aGVyIHRoZSBjYWxsIGlzIGEgbWF0ZSBhdHRyYWN0aW9uIHNpZ25hbCBvciBhbiBhZ2dyZXNzaXZlIHNpZ25hbC4KClRoZSBwcm9jZWR1cmUgaXMgYWxtb3N0IHRoZSBzYW1lIGFzIGZvciB0aGUgc3lsbGFibGVzIGFzIGFib3ZlLiBXaGF0IGlzIGRpZmZlcmVudCBpcyB0aGF0IGluc3RlYWQgb2YgY29weWluZyB0aGUgc2lnbmFsIGZyb20gKip6ZkRhdGEqKiwgd2UgaW5zdGVhZCBtYWtlIHB1cmUgc2lsZW5jZSBieSBwdXR0aW5nIGluIGEgZmxhdCBzaWduYWwgd2l0aCBhbiBhbXBsaXR1ZGUgb2YgMC4KCldlIHVzZSBhIGxvb3AgZm9yIGVhY2ggb2YgdGhlIGVuZHMgKHdoaWNoIGlzIHRoZSBzdGFydCBvZiBlYWNoIHNpbGVuY2UpLgoKVG8gZG8gdGhpcywgbWFrZSBhIGxpc3Qgb2YgemVyb3Mgd2l0aCB0aGUgbGVuZ3RoIG9mIHRoZSBpbnRlcnZhbCBhbmQgZ2V0IHRoZSBsZW5ndGggb2YgdGhlIGludGVydmFsIGJ5IGdldHRpbmcgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgZW5kIG9mIHRoZSBzaWxlbmNlLCB3aGljaCBpcyB0aGUgJ3N0YXJ0JyBvZiB0aGUgbmV4dCBzeWxsYWJsZSwgYW5kIHRoZSBzdGFydCBvZiB0aGUgc2lsZW5jZSwgd2hpY2ggaXMgdGhlICdlbmQnIG9mIHRoZSBwcmV2aW91cyBzeWxsYWJsZS4gQ29tcGxpY2F0ZWQhPyEgTm90IHJlYWxseSEKCmBgYHtyfQpub3AgPSBjKCkKbm9wdGltbXkgPSBjKCkKbm9wdGltbSA9IGMoKQogCmZvciAoaiBpbiBzZXEoMixsZW5ndGgoZW5kcykpKSB7CiAgCiAgbm9wW1tqXV0gPSBsaXN0KHJlcCgxMjcsIHRpbWVzID0gc3RhcnRzW2pdIC0gZW5kc1tqLTFdKSkKICBub3B0aW1teVtbal1dID0gdGltW2VuZHNbai0xXTpzdGFydHNbal1dCiAgbm9wdGltbVtbal1dID0gc2VxKDEvRnMsICgxICsgZW5kc1tqXSAtIHN0YXJ0c1tqXSkvRnMsIDEvRnMpCiAgCn0KYGBgCgoKCiMjI0ZvciByZXZpZXc6IFlvdSBjYW4gZWFzaWx5IGdldCB0aGUgZGF0YSBmb3IgZWFjaCBzeWxsYWJsZS4KCioqc3lsbGFibGVbWzFdXSoqIGlzIHRoZSBkYXRhIGZvciB0aGUgZmlyc3Qgc3lsbGFibGUsIGFuZCAqKnN5bGxhYmxlW1syXV0qKiB0aGUgc2Vjb25kLgoKTGV0J3MgcGxvdCBhIHN5bGxhYmxlLgoKYGBge3J9CgpwYXIobWZyb3cgPSBjKDEsMikpCnNwZWNwbG90KHN5bGxhYmxlW1s3XV0sIEZzID0gemZGcywgb3ZscCA9IDkwKQpwbG90KHRpbW15W1s3XV0sIHN5bGxhYmxlW1s3XV0sIHR5cGUgPSAibCIsIGNvbCA9ICJSZWQiKQpgYGAKCiAKCkhvdyBtYW55IHN5bGxhYmxlcyBkaWQgd2UgZmluZD8/CgoqKmxlbmd0aChzeWxsYWJsZSkqKgoKSG93IGxvbmcgKGR1cmF0aW9uKSBpcyBzeWxsYWJsZXs3fT8KCioqbGVuZ3RoKHN5bGxhYmxlW1s3XV0pL0ZzKioKCiMgRXhlcmNpc2UgMTA6IE1ha2UgYSBORVcgc29uZyB3aXRoIHRoZSBzeWxsYWJsZSBvcmRlciByZXZlcnNlZAoKSGVyZSB3ZSBhcmUgZ29pbmcgdG8gdGFrZSBlYWNoIHN5bGxhYmxlLCBhbmQgaW5zdGVhZCBvZiB0aGUgbm9ybWFsIG9yZGVyIG9mIEFCQ0RFRkcgd2Ugd2lsbCBtYWtlIHRoZSBvcmRlciBHRkVEQ0JBLiBFYXN5IQoKSGVyZSB3ZSBhcmUgbWFraW5nIGEgbmV3IHZhcmlhYmxlLCAqKnJldm9yZGVyc29uZyoqIGFuZCB3ZSBhcmUgcHV0dGluZyB0aGUgbGFzdCBzeWxsYWJsZSBpbiB0aGVyZS4KCmBgYHtyfQpyZXZvcmRlcnNvbmcgPSBzeWxsYWJsZVtsZW5ndGgoc3lsbGFibGUpXQpyZXZvcmRlcnNvbmcgPSB1bmxpc3QocmV2b3JkZXJzb25nKQoKYGBgCgpOb3cgd2Ugd2lsbCBjeWNsZSB0aG91Z2ggZWFjaCBzeWxsYWJsZSBmcm9tIHRoZSBzZWNvbmQgdG8gbGFzdCB0byB0aGUgZmlyc3QgKHdlIGFscmVhZHkgaGF2ZSB0aGUgbGFzdCBzeWxsYWJsZSBmcm9tIHRoZSBhYm92ZSBsaW5lLgoKVGhlIHZhcmlhYmxlcyBpbiB0aGUgYnJhY2tldHMgd2lsbCBiZSBjb25jYXRlbmF0ZWQuIFNvIFsgcmV2b3JkZXJzb25nIG5vcCBzeWxsYWJsZSBdIHdpbGwgbWFrZSB0aGUgcHJldmlvdXNseSBkZWZpbmVkICoqcmV2b3JkZXJzb25nKiogZm9sbG93ZWQgYnkgdGhlIHNpbGVudCBwZXJpb2QsIHdoaWNoIGlzIHRoZW4gZm9sbG93ZWQgYnkgdGhlIG5leHQgc3lsbGFibGUuIAoKYGBge3J9CmZvciAoayBpbiBzZXEobGVuZ3RoKHN5bGxhYmxlKS0xLCAxLCAtMSkpIHsKICAKICByZXZvcmRlcnNvbmcgPSBhcHBlbmQocmV2b3JkZXJzb25nLCB1bmxpc3Qobm9wW1trICsgMV1dKSkKICByZXZvcmRlcnNvbmcgPSBhcHBlbmQocmV2b3JkZXJzb25nLCB1bmxpc3Qoc3lsbGFibGVbW2tdXSkpCiAgCn0KCmBgYAoKVGhpcyBpcyBuaWNlIHRvIGFkZCAtIGEgdGltZSBiYXNlIGZvciB0aGlzIG5ldyBzaWduYWwuIFdlJ3ZlIHNlZW4gdGhpcyBtYW55IHRpbWVzIGJ5IG5vdy4KCmBgYHtyfQpyZXZ0aW0gPSBzZXEoMS9GcywgbGVuZ3RoKHJldm9yZGVyc29uZykvRnMsIDEvRnMpCmBgYAoKYGBge3J9CnAxID0gZ2dwbG90KCkgKyAKICBnZW9tX2xpbmUoYWVzKHggPSB0aW0sIHkgPSB6Zl9kYXRhQGxlZnQpLCBjb2xvciA9ICJCbHVlIikgKwogIHNjYWxlX3lfY29udGludW91cyhuYW1lID0gIkZyZXF1ZW5jeSIsIGJyZWFrcyA9IGMoMjcsMTI3LDIyNyksIGxpbWl0cyA9IGMoMCwyNTApKSArCiAgc2NhbGVfeF9jb250aW51b3VzKG5hbWUgPSAiVGltZSIpCgpwMiA9IGdncGxvdCgpICsKICBnZW9tX2xpbmUoYWVzKHggPSByZXZ0aW0sIHkgPSByZXZvcmRlcnNvbmcpLCBjb2xvciA9ICJSZWQiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKG5hbWUgPSAiRnJlcXVlbmN5IiwgYnJlYWtzID0gYygyNywxMjcsMjI3KSwgbGltaXRzID0gYygwLDI1MCkpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobmFtZSA9ICJUaW1lIikKCmdyaWQuYXJyYW5nZShwMSwgcDIsIG5yb3cgPSAyKQpgYGAKCm9yIHdlIGNhbiBzaG93IHRoZSBzcGVjdHJhLi4uCgp0aGUgb3JpbmluYWwgc2lnbmFsOiAKCmBgYHtyfQpzcGVjcGxvdCh6Zl9kYXRhLCBGcyA9IHpmRnMpCmBgYAoKcmV2ZXJzZSBzaWduYWw6IAoKYGBge3J9CnNwZWNwbG90KHJldm9yZGVyc29uZywgRnMgPSB6ZkZzKQpgYGAKClNhdmUgdGhlIGRhdGEgdG8gYSB3YXZlIGZpbGUuIFlvdSBjYW4gdGhlbiB1c2UgQXVkYWNpdHkgdG8gbGlzdGVuIHRvIGl0LiAKYGBge3J9CnNhdmV3YXYocmV2b3JkZXJzb25nLCBGcywgInJldm9yZGVyc29uZy53YXYiKQpgYGAKCiMgRXhlcmNpc2UgMTE6IE1ha2UgYSBORVcgc29uZyB3aXRoIGVhY2ggc3lsbGFibGUgcmV2ZXJzZWQgYnV0IHRoZSBzeWxsYWJsZSBvcmRlciBub3JtYWwKClRoaXMgaXMgbmVhcmx5IGlkZW50aWNhbCB0byB0aGUgcHJldmlvdXMgZXhjZXJpc2UsIGJ1dCBub3cgdGhlIG9yZGVyIGlzIGNvcnJlY3QgKEFCQ0RFRikgYnV0IGVhY2ggc3lsbGFibGUgaXMgcmV2ZXJzZWQuIAoKVGFrZSB0aGUgZmlyc3Qgc3lsbGFibGUgYW5kIHB1dCBpdCBpbiBvdXIgbmV3IHZhcmlhYmxlICoqcmV2c3lsc29uZyoqLi4uCgpgYGB7cn0KcmV2c3lsc29uZyA9IHVubGlzdChzeWxsYWJsZVtsZW5ndGgoc3lsbGFibGUpXSkKYGBgCgpOb3cgcmV2ZXJzZSBpdCAtLSB3ZSBzYXcgaG93IHRvIGRvIHRoaXMgaW4gYSBwcmV2aW91cyBleGNlcmlzZS4uLgoKYGBge3J9CnJldnN5bHNvbmcgPSByZXZzeWxzb25nW3NlcSh0byA9IDEsIGJ5ID0gLTEsIGxlbmd0aC5vdXQgPSBsZW5ndGgocmV2c3lsc29uZykpXQpgYGAKCk5vdyBkbyB0aGUgc2FtZSBmb3IgdGhlIHJlc3Qgb2YgdGhlIHN5bGxhYmxlcywgYWRkaW5nIHRoZW0gdG8gdGhlIG5ldyB2YXJpYWJsZS4uLgoKKldlIHVzZSB0aGUgdGVtcG9yYXJ5IHZhcmlhYmxlICoqcmEqKiB0byBkbyB0aGUgcmV2ZXJzaW5nKgoKYGBge3J9CmZvciAobCBpbiBzZXEoMiwgbGVuZ3RoKHN5bGxhYmxlKSwgMSkpIHsKICAKICByYSA9IHVubGlzdChzeWxsYWJsZVtbbF1dKQogIHJldnN5bHNvbmcgPSBhcHBlbmQocmV2c3lsc29uZywgdW5saXN0KG5vcFtbbF1dKSkKICByZXZzeWxzb25nID0gYXBwZW5kKHJldnN5bHNvbmcsIHVubGlzdChyYVtzZXEodG8gPSAxLCBieSA9IC0xLCBsZW5ndGgub3V0ID0gbGVuZ3RoKHN5bGxhYmxlW1tsXV0pKV0pKQogIAp9CmBgYAoKYW5kIGRvbid0IGZvcmdldCBhYm91dCB0aGUgdGltZSBkYXRhLi4uCgpgYGB7cn0KcmV2c3lsdGltID0gc2VxKDEvRnMsIGxlbmd0aChyZXZzeWxzb25nKS9GcywgMS9GcykKYGBgCgpOb3cgd2UgY2FuIG1ha2UgdGhlIHNhbWUgcGxvdHMgYXMgbGFzdCB0aW1lLCBpbmNsdWRpbmcgc3BlY3Ryb2dyYW1zOiAKCmBgYHtyfQpwMyA9IGdncGxvdCgpICsgCiAgZ2VvbV9saW5lKGFlcyh4ID0gdGltLCB5ID0gemZfZGF0YUBsZWZ0KSwgY29sb3IgPSAiQmx1ZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobmFtZSA9ICJGcmVxdWVuY3kiLCBicmVha3MgPSBjKDI3LDEyNywyMjcpLCBsaW1pdHMgPSBjKDAsMjUwKSkgKwogIHNjYWxlX3hfY29udGludW91cyhuYW1lID0gIlRpbWUiKQoKcDQgPSBnZ3Bsb3QoKSArCiAgZ2VvbV9saW5lKGFlcyh4ID0gcmV2c3lsdGltLCB5ID0gcmV2c3lsc29uZyksIGNvbG9yID0gIlJlZCIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobmFtZSA9ICJGcmVxdWVuY3kiLCBicmVha3MgPSBjKDI3LDEyNywyMjcpLCBsaW1pdHMgPSBjKDAsMjUwKSkgKwogIHNjYWxlX3hfY29udGludW91cyhuYW1lID0gIlRpbWUiKQoKZ3JpZC5hcnJhbmdlKHAzLCBwNCwgbnJvdyA9IDIpCmBgYAoKV2UgY2FuIHNhdmUgdGhlIGZpbGUgYXMgYSAud2F2IHVzaW5nIHRoZSBzYW1lIHByb2Nlc3MgYXMgaW4gdGhlIGxhc3QgZXhjZXJjaXNlLiAKCmBgYHtyfQpzYXZld2F2KHJldnN5bHNvbmcsIEZTLCAicmV2c3lsc29uZy53YXYiKQpgYGAKCldpdGggdGhlc2UgdG9vbHMgeW91IGNhbiBtYWtlIGEgd2lkZSByYW5nZSBvZiBhcmJpdHJhcnkgc3RpbXVsaSBmcm9tIHlvdXIgcmVjb3JkaW5ncyBvZiBhbmltYWwgc2lnbmFscy4gVGhlc2UgYXJlIHBvd2VyZnVsIHRvbGxzIGZvciBzZW5zb3J5IGVsZWN0cm9waHlzaW9sb2d5LiAKCiMjI1ByYWN0aWNlOlRyeSB0byByZWRvIHRoaXMgZXhlcmNpc2Ugd2l0aCB0aGUgZGF0YSBpbiB3Y3Mud2F2LiAKI0V4ZXJjaXNlIDEyOiBTcGlrZXMgCgpJbiB0aGlzIGV4ZXJjaXNlIHdlIHdpbGwgdGFrZSBhIHJlY29yZGluZyBvZiBhIG5ldXJhbCBhY3Rpdml0eSAoc3Bpa2VzISkgYW5kIHR1cm4gaXQgaW50byBkYXRhIHRoYXQgd2UgY2FuIHN0YXJ0IHRvIGFuYWx5emUgaW4gdXNlZnVsIHdheXMuIAoKSSBoYXZlIHByb3ZpZGVkIHR3byBzYW1wbGUgZmlsZXMgdGhhdCBjb250YWluIHNvbWUgbmljZSBuZXVyYWwgYWN0aXZpdHkuIFRoZXkgYXJlIHZlcnkgZGlmZmVyZW50IGZyb20gZWFjaCBvdGhlci4gCgpSZWFkIHRoZSBmaXJzdCBmaWxlLCBtYWtlIHRoZSB0aW1lIGRhdGEsIGFuZCBwbG90IChlYXN5IGJ5IG5vdykuIAoKYGBge3J9CmEgPSByZWFkV2F2ZSgic3Bpa2VzMS53YXYiKQphRnMgPSBhQHNhbXAucmF0ZQphdGltID0gc2VxKDEvYUZzLCBsZW5ndGgoYSkvYUZzLCAxL2FGcykKCmdncGxvdCgpICsKICBnZW9tX2xpbmUoYWVzKHggPSBhdGltLCB5ID0gYUBsZWZ0KSwgY29sb3VyID0gIkJsdWUiKQpgYGAKCkxldHMgem9vbSBpbiBvbiBhIHNlY29uZCBvZiB0aGUgZGF0YS4uLgoKKk5vdGU6IFRoaXMgY2FuIGJlIGRvbmUgc2V2ZXJhbCB3YXlzLiBUaGUgZmlyc3Qgd2F5IGlzIHRvIHVzICoqcCArIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDMsNCkpKiouIFRoaXMgd2F5IHdpbGwgcmVtb3ZlIGFsbCBwb2ludHMgbm90IHBsb3R0ZWQuIFlvdSBjYW4gdXNlICoqeGxpbSgzLDQpKiogYXMgYSBzaG9ydGhhbmQgdmVyc2lvbiBvZiB0aGlzIHNhbWUgZnVuY3Rpb24uIFRoZSBtZXRob2Qgd2UgdXNlIGlzICoqcCArIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygzLDQpKSoqLCB3aGljaCBQUkVTRVJWRVMgZGF0YSBwb2ludHMgbm90IHBsb3R0ZWQuIFRoaXMgbWF5IGJlIHJlbGV2YW50IGlmIHlvdSBhcmUgZml0dGluZyBhbnl0aGluZyB0byB0aGUgZGF0YS4qCgpgYGB7cn0KZ2dwbG90KCkgKwogIGdlb21fbGluZShhZXMoeCA9IGF0aW0sIHkgPSBhQGxlZnQpLCBjb2xvdXIgPSAiQmx1ZSIpICsgCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDMsNCkpCmBgYAoKTm93IGxldHMgc2V0IGFuZCBhbXBsaXR1ZGUgdGhyZXNob2xkIGZvciB0aGUgc3Bpa2VzLiBEaWZmZXJlbnQgcmVjb3JkaW5ncyBoYXZlIGRpZmZlcmVudCBhbXBsaXR1ZGVzZm9yIHNwaWtlcy4gSGVyZSBJIGd1ZXNzZWQgKioxNTAwMCoqLiBMZXRzIGxvb2sgYXQgdGhlIHBsb3QgdG8gY29uZmlybS4gCgpgYGB7cn0KYXRocmVzaCA9IDE1MDAwCmBgYAoKV2Ugd2lsbCB1c2UgdGhlICoqd2hpY2goKSoqIGZ1bmN0aW9uIHRvIGdldCBhbGwgb2YgdGhlIGRhdGEgYWJvdmUgdGhlIHRocmVzaG9sZC4gCgpgYGB7cn0Kc3Bpa2VzID0gd2hpY2goYUBsZWZ0ID4gYXRocmVzaCkKYGBgCgpUaGlzIGlzIGEgY29tcGxpY2F0ZWQsIGJ1dCBuZWNlc3Nhcnkgc3RlcC4gU3Bpa2VzIGFyZSBtb3JlIHRoYW4gMSBvciB0d28gbWlsbGlzZWNvbmRzIGluIGR1cmF0aW9uLiBUaHVzLCB3ZSB3YW50IGEgdGhyZXNob2xkIGZvciB0aGUgaW50ZXJ2YWwgYmV0d2VlbiBzcGlrZXMgLSBhbnl0aGluZyBsZXNzIHRoYW4gdGhhdCB0aHJlc2hvbGQgaXMgYSBtaXN0YWtlIGZyb20gdGhlICoqd2hpY2goKSogY29tbWFuZC4KCk91ciB0aHJlc2hvbGQgd2lsbCBiZSAxIG1pbGxpc2Vjb25kOiAwLjAwMSBzZWNvbmRzLiBTaW5jZSBhcmUgZGF0YSBpcyBpbiBzYW1wbGVzIGFuZCBub3QgaW4gbWlsbGlzZWNvbmRzLCB3ZSdsbCBzZXQgb3VyIHRocmVzaG9sZCB0byB0aGUgKipzYW1wbGUgcmF0ZSAqIG1pbGxpc2Vjb25kcyoqLgoKKipkaWZmKiogaXMgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBhZGphY2VudCBkYXRhIHBvaW50cy4KClNvLCBoZXJlIHdlIGZpbmQgYWxsIG9mIHRoZSBpbnN0YW5jZXMgd2hlcmUgdGhlIG51bWJlciBvZiBzYW1wbGVzIGJldHdlZW4gc3Bpa2VzIGlzIGdyZWF0ZXIgdGhhbiBvdXIgdGhyZXNob2xkIG9mIDEgbWlsbGlzZWNvbmQgc2FtcGxlcyAod2hpY2ggZm9yIEZzID0gMTAwMDAgaXMgMTAgc2FtcGxlcykuCgpgYGB7cn0Kc3Bpa2VzMSA9IHNwaWtlc1t3aGljaChkaWZmKHNwaWtlcykgPiAwLjAwMSAqIGFGcyldCmBgYAoKSG93IG1hbnkgc3Bpa2VzIGRpZCB3ZSBmaW5kPwoKYGBge3J9Cmxlbmd0aChzcGlrZXMxKQpgYGAKCldoYXQgaXMgdGhlIHNwaWtlIHJhdGU/CgpgYGB7cn0KbGVuZ3RoKHNwaWtlczEpL2F0aW1bbGVuZ3RoKGF0aW0pXQpgYGAKCk5vdyBsZXQncyBwbG90LiBXZSdsbCBwbG90IGEgU1RBUiBhdCBlYWNoIHRpbWUgdGhhdCBhIHNwaWtlIG9jY3VycmVkLiBXZSdsbCB1c2UgdGhlICoqcmVwKCkqKiBmdW5jdGlvbiwgZXhjZXB0IHRoaXMgdGltZSB3ZSB3aWxsIGNyZWF0ZSBhIGxpc3Qgb2Ygb25lcy4gVGhpcyB3b3VsZCBiZSBPSywgYnV0IEkgZGVjaWRlZCB0byBtdWx0aXBseSBpdCBieSB0aGUgdGhyZXNob2xkIHNvIHRoYXQgdGhlIGRvdHMgd2lsbCBiZSBhdCB0aGUgbGV2ZWwgb2YgdGhlIHRocmVzaG9sZCB3aGVuIHdlIHBsb3QgaXQuIHlvdSB3aWxsIHNlZS4uLgoKYGBge3J9CnlzID0gcmVwKDEsIGxlbmd0aChzcGlrZXMxKSkgKiBhdGhyZXNoCmBgYAoKTm93IHBsb3QuLi4KCmBgYHtyfQpnZ3Bsb3QoKSArCiAgZ2VvbV9saW5lKGFlcyh4ID0gYXRpbSwgeSA9IGFAbGVmdCksIGNvbG91ciA9ICJCbHVlIikKYGBgCgouLi5hbmQgYWRkIHRoZSBzdGFycy4KCmBgYHtyfQpnZ3Bsb3QoKSArCiAgZ2VvbV9saW5lKGFlcyh4ID0gYXRpbSwgeSA9IGFAbGVmdCksIGNvbG91ciA9ICJCbHVlIikgKwogIGdlb21fcG9pbnQoYWVzKHggPSBhdGltW3NwaWtlczFdLCB5ID0geXMpLCBjb2xvdXIgPSAiUmVkIiwgc2l6ZSA9IDIsIHNoYXBlID0gMSkKYGBgCgpab29tIGluIHRvIGdldCBhIGJldHRlciBsb29rIQoKYGBge3J9CmdncGxvdCgpICsKICBnZW9tX2xpbmUoYWVzKHggPSBhdGltLCB5ID0gYUBsZWZ0KSwgY29sb3VyID0gIkJsdWUiKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IGF0aW1bc3Bpa2VzMV0sIHkgPSB5cyksIGNvbG91ciA9ICJSZWQiLCBzaXplID0gMiwgc2hhcGUgPSAxKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDEsMikpCmBgYAoKV2UgY2FuIGdvIGV2ZW4gZnVydGhlci4uLgoKYGBge3J9CmdncGxvdCgpICsKICBnZW9tX2xpbmUoYWVzKHggPSBhdGltLCB5ID0gYUBsZWZ0KSwgY29sb3VyID0gIkJsdWUiKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IGF0aW1bc3Bpa2VzMV0sIHkgPSB5cyksIGNvbG91ciA9ICJSZWQiLCBzaXplID0gMiwgc2hhcGUgPSAxKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDEsMS4yNSkpCmBgYAoKQW5vdGhlciBpbXBvcnRhbnQgbWVhc3VyZSBpcyB0aGUgdGltZSBiZXR3ZWVuIHNwaWtlcy4gVGhpcyBpcyB2ZXJ5IGVhc3kgdG8gZ2V0IHVzaW5nIHRoZSAqKmRpZmYoKSoqIGNvbW1hbmQuCgoqKmRpZmYoKSoqIHdpbGwgZ2l2ZSB1c2UgdGhlIG51bWJlciBvZiBzYW1wbGVzIGJldHdlZW4gc3Bpa2VzLCBhbmQgdGhlbiB3ZSBkaXZpZGUgYnkgdGhlIHNhbXBsZSByYXRlIHRvIGdldCB0aGUgdGltZSwgaW4gc2Vjb25kcywgYmV0d2VlbiBzcGlrZXMuCgpgYGB7cn0KaW50ZXJ2YWxzMSA9IGRpZmYoc3Bpa2VzMSkvYUZzCmBgYAoKSGVyZSBhcmUgc29tZSBlYXN5IG1lYXN1cmVzLiAKCmBgYHtyfQptZWFuKGludGVydmFsczEpCnBtYXgoaW50ZXJ2YWxzMSkKcG1pbihpbnRlcnZhbHMxKQoKYGBgCgpCdXQgbG9va2luZyBhdCB0aGUgcGxvdCB3ZSBzZWUgdGhhdCB0aGUgc3Bpa2VzIGFyZSBpbiAiYnVyc3RzIiByYXRoZXIgdGhhbiByYW5kb21seSBzcHJlYWQuIElmIHRoZXkgd2VyZSByYW5kb21seSB0aW1lZCwgd2Ugd291bGQgZmluZCBhIHJhbmRvbSBkaXN0cmlidXRpb24gb2YgdGhlIGludGVydmFscyBiZXR3ZWVuIHNwaWtlcy4gTGV0cyB0YWtlIGEgbG9vayB1c2luZyBhIGhpc3RvZ3JhbS4uLgoKYGBge3J9CmdncGxvdCgpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IGludGVydmFsczEpLCBiaW5zID0gMTAwKQpgYGAKCkZvciB0aGVzZSBkYXRhIHdlIHNlZSB0aGF0IHRoZXJlIGFyZSBtYW55IG1hbnkgaW50ZXJ2YWxzIGJlbG93IDAuMDE1ICgxNSBtaWxsaXNlY29uZHMpLCBhbmQgYSBzcHJlYWQgb2YgbG9uZ2VyIGludGVydmFscy4gVGhpcyBwbG90IGlzIGEgdmVyeSBnb29kIGV4YW1wbGUgb2YgYSAiYnVyc3R5IiBuZXVyb24uCgpUaGUgb3RoZXIgZGF0YSwgc3Bpa2VzMi53YXYsIGlzIHZlcnkgdmVyeSBkaWZmZXJlbnQuCgpQbGVhc2UgZG8gdGhlIHNhbWUgYW5hbHlzaXMgZm9yIHRob3NlIGRhdGEgYXMgeW91IGRpZCBmb3IgKipzcGlrZXMxLndhdioqLiBVc2UgZGlmZmVyZW50IHZhcmlhYmxlIG5hbWVzIGxpa2UgY2hhbmdpbmcgKipzcGlrZXMxKiogdG8gKipzcGlrZXMyKiogYW5kICoqaW50ZXJ2YWwxKiogdG8gKippbnRlcnZhbDIqKiBzbyB0aGF0IHlvdSBjYW4gcGxvdCBhIGhpc3RvZ3JhbSBqdXN0IGxpa2Ugd2UgZGlkIGFib3ZlLiBUcnkgcGxvdHRpbmcgYm90aCBoaXN0b2dyYW1zIGluIHRoZSBzYW1lIGZyYW1lLiA=